package cz.zcu.fav.ratdroid.service;

import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.springframework.http.ContentCodingType;
import org.springframework.http.HttpAuthentication;
import org.springframework.http.HttpBasicAuthentication;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;

import android.app.ProgressDialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.AsyncTask;
import android.preference.PreferenceManager;
import cz.zcu.fav.ratdroid.R;
import cz.zcu.fav.ratdroid.activity.SettingsActivity;

public class RestTask<TT> extends AsyncTask<String, Void, TT>
{

	Logger logger = Logger.getLogger("RestTask");
	Context context;
	ProgressDialog dialog;
	RestTemplate restTemplate;
	IRestListener<TT> listener;
	private Class<TT> type;
	private HttpMethod httpMethod;
	private TT body;
	private SharedPreferences pref;

	public RestTask(Context context, RestTemplate restTemplate, Class<TT> type,
			IRestListener<TT> listener, HttpMethod httpMethod)
	{
		this.context = context;
		this.restTemplate = restTemplate;
		this.type = type;
		this.listener = listener;
		this.httpMethod = httpMethod;
	}

	public void setContext(Context context)
	{
		this.context = context;
	}

	/**
	 * 
	 * In case we want to send some body within a request.
	 * 
	 * @param context
	 * @param restTemplate
	 * @param type
	 * @param listener
	 * @param httpMethod
	 * @param body
	 */
	public RestTask(Context context, RestTemplate restTemplate, Class<TT> type,
			IRestListener<TT> listener, HttpMethod httpMethod, TT body)
	{
		this(context, restTemplate, type, listener, httpMethod);
		this.body = body;
	}

	@Override
	protected void onPreExecute()
	{
		super.onPreExecute();
		if (context != null)
		{
			dialog = ProgressDialog.show(context,
					context.getString(R.string.loading),
					context.getString(R.string.pleasewait), true);

			pref = PreferenceManager.getDefaultSharedPreferences(context);
		} else
			cancel(true);
	}

	@Override
	protected void onPostExecute(TT result)
	{
		super.onPostExecute(result);
		if (context != null)
		{
			try
			{
				dialog.dismiss();
			} catch (Exception ex)
			{
				logger.log(Level.WARNING,
						"screen is probably already gone! Discard results", ex);
			}
			listener.notifyDataLoaded(result);
		}
	}

	@Override
	protected TT doInBackground(String... params)
	{
		try
		{
			String url = params[0];
			HttpEntity<TT> requestEntity;

			if (!pref.contains(RestTaskFactory.REST_COOKIE_KEY))
			{
				// we are not authorized
				requestEntity = new HttpEntity<TT>(body,
						getAuthenticatedRequestHeaders(pref.getString(
								SettingsActivity.KEY_PREF_USERNAME, null),
								pref.getString(
										SettingsActivity.KEY_PREF_PASSWORD,
										null)));

			} else
			{
				HttpHeaders headers = getNotAuthenticatedRequestHeaders();
				headers.set(RestTaskFactory.REST_COOKIE_KEY,
						pref.getString(RestTaskFactory.REST_COOKIE_KEY, ""));
				requestEntity = new HttpEntity<TT>(body, headers);
			}
			ResponseEntity<TT> response = null;
			try
			{
				response = restTemplate.exchange(url, httpMethod,
						requestEntity, type);

			} catch (HttpClientErrorException ex)
			{
				if (ex.getStatusCode() == HttpStatus.UNAUTHORIZED)
				{
					requestEntity = new HttpEntity<TT>(body,
							getAuthenticatedRequestHeaders(pref.getString(
									SettingsActivity.KEY_PREF_USERNAME, null),
									pref.getString(
											SettingsActivity.KEY_PREF_PASSWORD,
											null)));

					response = restTemplate.exchange(url, httpMethod,
							requestEntity, type);
					// restTemplate.pu
				} else
				{
					throw ex;
				}
			}

			HttpHeaders rHeaders = response.getHeaders();
			List<String> val = rHeaders.get("Set-Cookie");
			if (null != val)
			{
				for (String cookie : val)
				{
					if (cookie.contains(RestTaskFactory.REST_COOKIE_KEY))
					{
						Editor ed = pref.edit();
						ed.putString(RestTaskFactory.REST_COOKIE_KEY, cookie);
						ed.commit();
						break;
					}
				}
			}

			return (TT) response.getBody();
		} catch (Exception ex)
		{
			logger.log(Level.SEVERE, ex.getMessage(), ex);
			listener.notifyException(ex);
			return null;
		}

	}

	protected HttpHeaders getAuthenticatedRequestHeaders(String username,
			String password)
	{

		// Set the BasicAuth needed for server
		HttpAuthentication authHeader = new HttpBasicAuthentication(username,
				password);
		HttpHeaders requestHeaders = getNotAuthenticatedRequestHeaders();
		requestHeaders.setAuthorization(authHeader);
		return requestHeaders;
	}

	protected HttpHeaders getNotAuthenticatedRequestHeaders()
	{

		HttpHeaders requestHeaders = new HttpHeaders();
		// Set the Content-Type header
		requestHeaders.setContentType(MediaType.APPLICATION_XML);
		requestHeaders.setAccept(Collections.singletonList(new MediaType(
				"application", "xml")));
		requestHeaders.setAcceptEncoding(ContentCodingType.ALL);

		return requestHeaders;
	}

}
